{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "99a80181",
   "metadata": {},
   "source": [
    "# Introduction to the Planner\n",
    "\n",
    "The Planner is one of the fundamental concepts of the Semantic Kernel.\n",
    "\n",
    "It makes use of the collection of native and semantic functions that have been registered to the kernel and using AI, will formulate a plan to execute the given ask.\n",
    "\n",
    "From our own testing, planner works best with more powerful models like `gpt4` but sometimes you might get working plans with cheaper models like `gpt-35-turbo`. We encourage you to implement your own versions of the planner and use different models that fit your user needs.  \n",
    "\n",
    "Read more about planner [here](https://aka.ms/sk/concepts/planner)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07eb35d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "!python -m pip install semantic-kernel==0.3.12.dev0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11e59885",
   "metadata": {},
   "outputs": [],
   "source": [
    "import semantic_kernel as sk\n",
    "from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, AzureChatCompletion\n",
    "\n",
    "kernel = sk.Kernel()\n",
    "\n",
    "useAzureOpenAI = False\n",
    "\n",
    "# Configure AI backend used by the kernel\n",
    "if useAzureOpenAI:\n",
    "    \n",
    "    deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n",
    "    kernel.add_chat_service(\"gpt-3.5\", AzureChatCompletion(deployment, endpoint, api_key))\n",
    "else:\n",
    "    api_key, org_id = sk.openai_settings_from_dot_env()\n",
    "    kernel.add_chat_service(\"gpt-3.5\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ff28070",
   "metadata": {},
   "source": [
    "## It all begins with an ask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93bc6103",
   "metadata": {},
   "outputs": [],
   "source": [
    "ask = \"\"\"\n",
    "Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French.\n",
    "Convert the text to uppercase\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5d86739",
   "metadata": {},
   "source": [
    "### Providing skills to the planner\n",
    "The planner needs to know what skills are available to it. Here we'll give it access to the `SummarizeSkill` and `WriterSkill` we have defined on disk. This will include many semantic functions, of which the planner will intelligently choose a subset. \n",
    "\n",
    "You can also include native functions as well. Here we'll add the TextSkill."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca0e7604",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.core_skills.text_skill import TextSkill\n",
    "\n",
    "skills_directory = \"../../samples/skills/\"\n",
    "summarize_skill = kernel.import_semantic_skill_from_directory(skills_directory, \"SummarizeSkill\")\n",
    "writer_skill = kernel.import_semantic_skill_from_directory(skills_directory, \"WriterSkill\")\n",
    "text_skill = kernel.import_skill(TextSkill(), \"TextSkill\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "deff5675",
   "metadata": {},
   "source": [
    "Define your ASK. What do you want the Kernel to do?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eee6fe7b",
   "metadata": {},
   "source": [
    "# Basic Planner"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "590a22f2",
   "metadata": {},
   "source": [
    " Let's start by taking a look at a basic planner. The `BasicPlanner` produces a JSON-based plan that aims to solve the provided ask sequentially and evaluated in order."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20d35ed0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.planning.basic_planner import BasicPlanner\n",
    "planner = BasicPlanner()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5697c09",
   "metadata": {},
   "outputs": [],
   "source": [
    "basic_plan = await planner.create_plan_async(ask, kernel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b425ba1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(basic_plan.generated_plan)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f3a48f8",
   "metadata": {},
   "source": [
    "You can see that the Planner took my ask and converted it into an JSON-based plan detailing how the AI would go about solving this task, making use of the skills that the Kernel has available to it.\n",
    "\n",
    "As you can see in the above plan, the AI has determined which functions to call in order to fulfill the user ask. The output of each step of the plan becomes the input to the next function."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd4df0c2",
   "metadata": {},
   "source": [
    "Let's also define an inline skill and have it be available to the Planner. Be sure to give it a function name and skill name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54422ba6",
   "metadata": {},
   "outputs": [],
   "source": [
    "sk_prompt = \"\"\"\n",
    "{{$input}}\n",
    "\n",
    "Rewrite the above in the style of Shakespeare.\n",
    "\"\"\"\n",
    "shakespeareFunction = kernel.create_semantic_function(sk_prompt, \"shakespeare\", \"ShakespeareSkill\",\n",
    "                                                      max_tokens=2000, temperature=0.8)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5057cf9b",
   "metadata": {},
   "source": [
    "Let's update our ask using this new skill"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3161dcf",
   "metadata": {},
   "outputs": [],
   "source": [
    "ask = \"\"\"\n",
    "Tomorrow is Valentine's day. I need to come up with a few date ideas.\n",
    "She likes Shakespeare so write using his style. She speaks French so write it in French.\n",
    "Convert the text to uppercase.\"\"\"\n",
    "\n",
    "new_plan = await planner.create_plan_async(ask, kernel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "997462e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(new_plan.generated_plan)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b67a052e",
   "metadata": {},
   "source": [
    "### Executing the plan"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b839c90",
   "metadata": {},
   "source": [
    "Now that we have a plan, let's try to execute it! The Planner has a function called `execute_plan`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9384831a",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = await planner.execute_plan_async(new_plan, kernel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9192b186",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8a9b6b7",
   "metadata": {},
   "source": [
    "# The Plan Object Model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e50f8859",
   "metadata": {},
   "source": [
    "To build more advanced planners, we need to introduce a proper Plan object that can contain all the necessary state and information needed for high quality plans.\n",
    "\n",
    "To see what that object model is, look at (https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/planning/plan.py)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a0cb2a2",
   "metadata": {},
   "source": [
    "# Sequential Planner"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1c66d83",
   "metadata": {},
   "source": [
    "The sequential planner is an XML-based step-by-step planner. You can see the prompt used for it here (https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/planning/sequential_planner/Skills/SequentialPlanning/skprompt.txt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e2e90624",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.planning import SequentialPlanner\n",
    "planner = SequentialPlanner(kernel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d537981",
   "metadata": {},
   "outputs": [],
   "source": [
    "sequential_plan = await planner.create_plan_async(goal=ask)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee2f462b",
   "metadata": {},
   "source": [
    "To see the steps that the Sequential Planner will take, we can iterate over them and print their descriptions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7007418",
   "metadata": {},
   "outputs": [],
   "source": [
    "for step in sequential_plan._steps:\n",
    "    print(step.description, \":\", step._state.__dict__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4db5f844",
   "metadata": {},
   "source": [
    "Let's ask the sequential planner to execute the plan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88411884",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = await sequential_plan.invoke_async()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36d27aa0",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6487c75",
   "metadata": {},
   "source": [
    "# Action Planner"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b045e26b",
   "metadata": {},
   "source": [
    "The action planner takes in a list of functions and the goal, and outputs a **single** function to use that is appropriate to meet that goal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5bfc0b9f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.planning import ActionPlanner\n",
    "planner = ActionPlanner(kernel)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53b1f296",
   "metadata": {},
   "source": [
    "Let's add more skills to the kernel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc12642a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.core_skills import FileIOSkill, MathSkill, TextSkill, TimeSkill\n",
    "kernel.import_skill(MathSkill(), \"math\")\n",
    "kernel.import_skill(FileIOSkill(), \"fileIO\")\n",
    "kernel.import_skill(TimeSkill(), \"time\")\n",
    "kernel.import_skill(TextSkill(), \"text\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b938dc0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "ask = \"What is the sum of 110 and 990?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3aafd268",
   "metadata": {},
   "outputs": [],
   "source": [
    "plan = await planner.create_plan_async(goal=ask)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42589835",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = await plan.invoke_async()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc75e7a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "789b651a",
   "metadata": {},
   "source": [
    "# Stepwise Planner"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a4bbcc3",
   "metadata": {},
   "source": [
    "Stepwise Planner is based off the paper from MRKL (Modular Reasoning, Knowledge and Language) and is similar to other papers like ReACT (Reasoning and Acting in Language Models). At the core, the stepwise planner allows for the AI to form \"thoughts\" and \"observations\" and execute actions based off those to achieve a user's goal. This continues until all required functions are complete and a final output is generated.\n",
    "\n",
    "See a video walkthrough of Stepwise Planner [here.](https://youtu.be/DG_Ge1v0c4Q?si=T1CHaAm1vV0mWRHu)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32839327",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.planning import StepwisePlanner\n",
    "from semantic_kernel.planning.stepwise_planner.stepwise_planner_config import (\n",
    "    StepwisePlannerConfig,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0a00bde",
   "metadata": {},
   "source": [
    "Let's create a Bing Search native skill that we can pass in to the Kernel.\n",
    "\n",
    "Make sure you have a Bing Search API key in your `.env` file\n",
    "\n",
    "(https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92fc5de8",
   "metadata": {},
   "outputs": [],
   "source": [
    "class WebSearchEngineSkill:\n",
    "    \"\"\"\n",
    "    A search engine skill.\n",
    "    \"\"\"\n",
    "    from semantic_kernel.orchestration.sk_context import SKContext\n",
    "    from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter\n",
    "\n",
    "    def __init__(self, connector) -> None:\n",
    "        self._connector = connector\n",
    "\n",
    "    @sk_function(\n",
    "        description=\"Performs a web search for a given query\", name=\"searchAsync\"\n",
    "    )\n",
    "    @sk_function_context_parameter(\n",
    "        name=\"query\",\n",
    "        description=\"The search query\",\n",
    "    )\n",
    "    async def search_async(self, query: str, context: SKContext) -> str:\n",
    "        query = query or context.variables.get(\"query\")[1]\n",
    "        result = await self._connector.search_async(query, num_results=5, offset=0)\n",
    "        return str(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "415f7876",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.connectors.search_engine import BingConnector\n",
    "\n",
    "BING_API_KEY = sk.bing_search_settings_from_dot_env()\n",
    "connector = BingConnector(BING_API_KEY)\n",
    "kernel.import_skill(WebSearchEngineSkill(connector), skill_name=\"WebSearch\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "effdf3ab",
   "metadata": {},
   "source": [
    "Let's also add a couple more skills"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abe150e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.core_skills.math_skill import MathSkill\n",
    "from semantic_kernel.core_skills.time_skill import TimeSkill\n",
    "\n",
    "kernel.import_skill(TimeSkill(), \"time\")\n",
    "kernel.import_skill(MathSkill(), \"math\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06d08549",
   "metadata": {},
   "outputs": [],
   "source": [
    "planner = StepwisePlanner(\n",
    "    kernel, StepwisePlannerConfig(max_iterations=10, min_iteration_time_ms=1000)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50699ec3",
   "metadata": {},
   "source": [
    "Now let's do a more complicated ask that will require planner to make a call to Bing to get the latest information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "596ade21",
   "metadata": {},
   "outputs": [],
   "source": [
    "ask = \"\"\"How many total championships combined do the top 5 teams in the NBA have?\"\"\"\n",
    "\n",
    "plan = planner.create_plan(goal=ask)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "176988ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = await plan.invoke_async()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d00c6f71",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb40370d",
   "metadata": {},
   "source": [
    "Let's see the steps that the AI took to get to the answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7159ca1b",
   "metadata": {},
   "outputs": [],
   "source": [
    "for index, step in enumerate(plan._steps):\n",
    "    print(\"Step:\", index)\n",
    "    print(\"Description:\",step.description)\n",
    "    print(\"Function:\", step.skill_name + \".\" + step._function.name)\n",
    "    if len(step._outputs) > 0:\n",
    "        print( \"  Output:\\n\", str.replace(result[step._outputs[0]],\"\\n\", \"\\n  \"))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
